/*
 * ! g.Raphael 0.5 - Charting library, based on Raphaël
 * 
 * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com) Licensed under
 * the MIT (http://www.opensource.org/licenses/mit-license.php) license.
 */

/*
 * Tooltips on Element prototype
 */
/*
 * \ Element.popup [ method ] * Puts the context Element in a 'popup' tooltip.
 * Can also be used on sets. * > Parameters * - dir (string) location of Element
 * relative to the tail: `'down'`, `'left'`, `'up'` [default], or `'right'`. -
 * size (number) amount of bevel/padding around the Element, as well as half the
 * width and height of the tail [default: `5`] - x (number) x coordinate of the
 * popup's tail [default: Element's `x` or `cx`] - y (number) y coordinate of
 * the popup's tail [default: Element's `y` or `cy`] * = (object) path element
 * of the popup > Usage | paper.circle(50, 50, 5).attr({ | stroke: "#fff", |
 * fill: "0-#c9de96-#8ab66b:44-#398235" | }).popup(); \
 */
Raphael.el.popup = function(dir, size, x, y) {
	var paper = this.paper || this[0].paper, bb, xy, center, cw, ch;

	if (!paper)
		return;

	switch (this.type) {
		case 'text' :
		case 'circle' :
		case 'ellipse' :
			center = true;
			break;
		default :
			center = false;
	}

	dir = dir == null ? 'up' : dir;
	size = size || 5;
	bb = this.getBBox();

	x = typeof x == 'number' ? x : (center ? bb.x + bb.width / 2 : bb.x);
	y = typeof y == 'number' ? y : (center ? bb.y + bb.height / 2 : bb.y);
	cw = Math.max(bb.width / 2 - size, 0);
	ch = Math.max(bb.height / 2 - size, 0);

	this.translate(x - bb.x - (center ? bb.width / 2 : 0), y - bb.y
					- (center ? bb.height / 2 : 0));
	bb = this.getBBox();

	var paths = {
		up : ['M', x, y, 'l', -size, -size, -cw, 0, 'a', size, size, 0, 0, 1,
				-size, -size, 'l', 0, -bb.height, 'a', size, size, 0, 0, 1,
				size, -size, 'l', size * 2 + cw * 2, 0, 'a', size, size, 0, 0,
				1, size, size, 'l', 0, bb.height, 'a', size, size, 0, 0, 1,
				-size, size, 'l', -cw, 0, 'z'].join(','),
		down : ['M', x, y, 'l', size, size, cw, 0, 'a', size, size, 0, 0, 1,
				size, size, 'l', 0, bb.height, 'a', size, size, 0, 0, 1, -size,
				size, 'l', -(size * 2 + cw * 2), 0, 'a', size, size, 0, 0, 1,
				-size, -size, 'l', 0, -bb.height, 'a', size, size, 0, 0, 1,
				size, -size, 'l', cw, 0, 'z'].join(','),
		left : ['M', x, y, 'l', -size, size, 0, ch, 'a', size, size, 0, 0, 1,
				-size, size, 'l', -bb.width, 0, 'a', size, size, 0, 0, 1,
				-size, -size, 'l', 0, -(size * 2 + ch * 2), 'a', size, size, 0,
				0, 1, size, -size, 'l', bb.width, 0, 'a', size, size, 0, 0, 1,
				size, size, 'l', 0, ch, 'z'].join(','),
		right : ['M', x, y, 'l', size, -size, 0, -ch, 'a', size, size, 0, 0, 1,
				size, -size, 'l', bb.width, 0, 'a', size, size, 0, 0, 1, size,
				size, 'l', 0, size * 2 + ch * 2, 'a', size, size, 0, 0, 1,
				-size, size, 'l', -bb.width, 0, 'a', size, size, 0, 0, 1,
				-size, -size, 'l', 0, -ch, 'z'].join(',')
	};

	xy = {
		up : {
			x : -!center * (bb.width / 2),
			y : -size * 2 - (center ? bb.height / 2 : bb.height)
		},
		down : {
			x : -!center * (bb.width / 2),
			y : size * 2 + (center ? bb.height / 2 : bb.height)
		},
		left : {
			x : -size * 2 - (center ? bb.width / 2 : bb.width),
			y : -!center * (bb.height / 2)
		},
		right : {
			x : size * 2 + (center ? bb.width / 2 : bb.width),
			y : -!center * (bb.height / 2)
		}
	}[dir];

	this.translate(xy.x, xy.y);

	return paper.path(paths[dir]).attr({
				fill : "#000",
				stroke : "none"
			}).insertBefore(this.node ? this : this[0]);
};

/*
 * \ Element.tag [ method ] * Puts the context Element in a 'tag' tooltip. Can
 * also be used on sets. * > Parameters * - angle (number) angle of orientation
 * in degrees [default: `0`] - r (number) radius of the loop [default: `5`] - x
 * (number) x coordinate of the center of the tag loop [default: Element's `x`
 * or `cx`] - y (number) y coordinate of the center of the tag loop [default:
 * Element's `x` or `cx`] * = (object) path element of the tag > Usage |
 * paper.circle(50, 50, 15).attr({ | stroke: "#fff", | fill:
 * "0-#c9de96-#8ab66b:44-#398235" | }).tag(60); \
 */
Raphael.el.tag = function(angle, r, x, y) {
	var d = 3, paper = this.paper || this[0].paper;

	if (!paper)
		return;

	var p = paper.path().attr({
				fill : '#000',
				stroke : '#000'
			}), bb = this.getBBox(), dx, R, center, tmp;

	switch (this.type) {
		case 'text' :
		case 'circle' :
		case 'ellipse' :
			center = true;
			break;
		default :
			center = false;
	}

	angle = angle || 0;
	x = typeof x == 'number' ? x : (center ? bb.x + bb.width / 2 : bb.x);
	y = typeof y == 'number' ? y : (center ? bb.y + bb.height / 2 : bb.y);
	r = r == null ? 5 : r;
	R = .5522 * r;

	if (bb.height >= r * 2) {
		p.attr({
					path : ["M", x, y + r, "a", r, r, 0, 1, 1, 0, -r * 2, r, r,
							0, 1, 1, 0, r * 2, "m", 0, -r * 2 - d, "a", r + d,
							r + d, 0, 1, 0, 0, (r + d) * 2, "L", x + r + d,
							y + bb.height / 2 + d, "l", bb.width + 2 * d, 0, 0,
							-bb.height - 2 * d, -bb.width - 2 * d, 0, "L", x,
							y - r - d].join(",")
				});
	} else {
		dx = Math.sqrt(Math.pow(r + d, 2) - Math.pow(bb.height / 2 + d, 2));
		p.attr({
					path : ["M", x, y + r, "c", -R, 0, -r, R - r, -r, -r, 0,
							-R, r - R, -r, r, -r, R, 0, r, r - R, r, r, 0, R,
							R - r, r, -r, r, "M", x + dx,
							y - bb.height / 2 - d, "a", r + d, r + d, 0, 1, 0,
							0, bb.height + 2 * d, "l",
							r + d - dx + bb.width + 2 * d, 0, 0,
							-bb.height - 2 * d, "L", x + dx,
							y - bb.height / 2 - d].join(",")
				});
	}

	angle = 360 - angle;
	p.rotate(angle, x, y);

	if (this.attrs) {
		// elements
		this.attr(
				this.attrs.x ? 'x' : 'cx',
				x
						+ r
						+ d
						+ (!center
								? this.type == 'text' ? bb.width : 0
								: bb.width / 2)).attr('y',
				center ? y : y - bb.height / 2);
		this.rotate(angle, x, y);
		angle > 90
				&& angle < 270
				&& this.attr(this.attrs.x ? 'x' : 'cx',
						x - r - d - (!center ? bb.width : bb.width / 2))
						.rotate(180, x, y);
	} else {
		// sets
		if (angle > 90 && angle < 270) {
			this.translate(x - bb.x - bb.width - r - d, y - bb.y - bb.height
							/ 2);
			this.rotate(angle - 180, bb.x + bb.width + r + d, bb.y + bb.height
							/ 2);
		} else {
			this.translate(x - bb.x + r + d, y - bb.y - bb.height / 2);
			this.rotate(angle, bb.x - r - d, bb.y + bb.height / 2);
		}
	}

	return p.insertBefore(this.node ? this : this[0]);
};

/*
 * \ Element.drop [ method ] * Puts the context Element in a 'drop' tooltip. Can
 * also be used on sets. * > Parameters * - angle (number) angle of orientation
 * in degrees [default: `0`] - x (number) x coordinate of the drop's point
 * [default: Element's `x` or `cx`] - y (number) y coordinate of the drop's
 * point [default: Element's `x` or `cx`] * = (object) path element of the drop >
 * Usage | paper.circle(50, 50, 8).attr({ | stroke: "#fff", | fill:
 * "0-#c9de96-#8ab66b:44-#398235" | }).drop(60); \
 */
Raphael.el.drop = function(angle, x, y) {
	var bb = this.getBBox(), paper = this.paper || this[0].paper, center, size, p, dx, dy;

	if (!paper)
		return;

	switch (this.type) {
		case 'text' :
		case 'circle' :
		case 'ellipse' :
			center = true;
			break;
		default :
			center = false;
	}

	angle = angle || 0;

	x = typeof x == 'number' ? x : (center ? bb.x + bb.width / 2 : bb.x);
	y = typeof y == 'number' ? y : (center ? bb.y + bb.height / 2 : bb.y);
	size = Math.max(bb.width, bb.height) + Math.min(bb.width, bb.height);
	p = paper.path(["M", x, y, "l", size, 0, "A", size * .4, size * .4, 0, 1,
			0, x + size * .7, y - size * .7, "z"]).attr({
				fill : "#000",
				stroke : "none"
			}).rotate(22.5 - angle, x, y);

	angle = (angle + 90) * Math.PI / 180;
	dx = (x + size * Math.sin(angle)) - (center ? 0 : bb.width / 2);
	dy = (y + size * Math.cos(angle)) - (center ? 0 : bb.height / 2);

	this.attrs ? this.attr(this.attrs.x ? 'x' : 'cx', dx).attr(
			this.attrs.y ? 'y' : 'cy', dy) : this.translate(dx - bb.x, dy
					- bb.y);

	return p.insertBefore(this.node ? this : this[0]);
};

/*
 * \ Element.flag [ method ] * Puts the context Element in a 'flag' tooltip. Can
 * also be used on sets. * > Parameters * - angle (number) angle of orientation
 * in degrees [default: `0`] - x (number) x coordinate of the flag's point
 * [default: Element's `x` or `cx`] - y (number) y coordinate of the flag's
 * point [default: Element's `x` or `cx`] * = (object) path element of the flag >
 * Usage | paper.circle(50, 50, 10).attr({ | stroke: "#fff", | fill:
 * "0-#c9de96-#8ab66b:44-#398235" | }).flag(60); \
 */
Raphael.el.flag = function(angle, x, y) {
	var d = 3, paper = this.paper || this[0].paper;

	if (!paper)
		return;

	var p = paper.path().attr({
				fill : '#000',
				stroke : '#000'
			}), bb = this.getBBox(), h = bb.height / 2, center;

	switch (this.type) {
		case 'text' :
		case 'circle' :
		case 'ellipse' :
			center = true;
			break;
		default :
			center = false;
	}

	angle = angle || 0;
	x = typeof x == 'number' ? x : (center ? bb.x + bb.width / 2 : bb.x);
	y = typeof y == 'number' ? y : (center ? bb.y + bb.height / 2 : bb.y);

	p.attr({
				path : ["M", x, y, "l", h + d, -h - d, bb.width + 2 * d, 0, 0,
						bb.height + 2 * d, -bb.width - 2 * d, 0, "z"].join(",")
			});

	angle = 360 - angle;
	p.rotate(angle, x, y);

	if (this.attrs) {
		// elements
		this.attr(
				this.attrs.x ? 'x' : 'cx',
				x
						+ h
						+ d
						+ (!center
								? this.type == 'text' ? bb.width : 0
								: bb.width / 2)).attr('y',
				center ? y : y - bb.height / 2);
		this.rotate(angle, x, y);
		angle > 90
				&& angle < 270
				&& this.attr(this.attrs.x ? 'x' : 'cx',
						x - h - d - (!center ? bb.width : bb.width / 2))
						.rotate(180, x, y);
	} else {
		// sets
		if (angle > 90 && angle < 270) {
			this.translate(x - bb.x - bb.width - h - d, y - bb.y - bb.height
							/ 2);
			this.rotate(angle - 180, bb.x + bb.width + h + d, bb.y + bb.height
							/ 2);
		} else {
			this.translate(x - bb.x + h + d, y - bb.y - bb.height / 2);
			this.rotate(angle, bb.x - h - d, bb.y + bb.height / 2);
		}
	}

	return p.insertBefore(this.node ? this : this[0]);
};

/*
 * \ Element.label [ method ] * Puts the context Element in a 'label' tooltip.
 * Can also be used on sets. * = (object) path element of the label. > Usage |
 * paper.circle(50, 50, 10).attr({ | stroke: "#fff", | fill:
 * "0-#c9de96-#8ab66b:44-#398235" | }).label(); \
 */
Raphael.el.label = function() {
	var bb = this.getBBox(), paper = this.paper || this[0].paper, r = Math.min(
			20, bb.width + 10, bb.height + 10)
			/ 2;

	if (!paper)
		return;

	return paper.rect(bb.x - r / 2, bb.y - r / 2, bb.width + r, bb.height + r,
			r).attr({
				stroke : 'none',
				fill : '#000'
			}).insertBefore(this.node ? this : this[0]);
};

/*
 * \ Element.blob [ method ] * Puts the context Element in a 'blob' tooltip. Can
 * also be used on sets. * > Parameters * - angle (number) angle of orientation
 * in degrees [default: `0`] - x (number) x coordinate of the blob's tail
 * [default: Element's `x` or `cx`] - y (number) y coordinate of the blob's tail
 * [default: Element's `x` or `cx`] * = (object) path element of the blob >
 * Usage | paper.circle(50, 50, 8).attr({ | stroke: "#fff", | fill:
 * "0-#c9de96-#8ab66b:44-#398235" | }).blob(60); \
 */
Raphael.el.blob = function(angle, x, y) {
	var bb = this.getBBox(), rad = Math.PI / 180, paper = this.paper
			|| this[0].paper, p, center, size;

	if (!paper)
		return;

	switch (this.type) {
		case 'text' :
		case 'circle' :
		case 'ellipse' :
			center = true;
			break;
		default :
			center = false;
	}

	p = paper.path().attr({
				fill : "#000",
				stroke : "none"
			});
	angle = (+angle + 1 ? angle : 45) + 90;
	size = Math.min(bb.height, bb.width);
	x = typeof x == 'number' ? x : (center ? bb.x + bb.width / 2 : bb.x);
	y = typeof y == 'number' ? y : (center ? bb.y + bb.height / 2 : bb.y);

	var w = Math.max(bb.width + size, size * 25 / 12), h = Math.max(bb.height
					+ size, size * 25 / 12), x2 = x + size
			* Math.sin((angle - 22.5) * rad), y2 = y + size
			* Math.cos((angle - 22.5) * rad), x1 = x + size
			* Math.sin((angle + 22.5) * rad), y1 = y + size
			* Math.cos((angle + 22.5) * rad), dx = (x1 - x2) / 2, dy = (y1 - y2)
			/ 2, rx = w / 2, ry = h / 2, k = -Math.sqrt(Math.abs(rx * rx * ry
			* ry - rx * rx * dy * dy - ry * ry * dx * dx)
			/ (rx * rx * dy * dy + ry * ry * dx * dx)), cx = k * rx * dy / ry
			+ (x1 + x2) / 2, cy = k * -ry * dx / rx + (y1 + y2) / 2;

	p.attr({
				x : cx,
				y : cy,
				path : ["M", x, y, "L", x1, y1, "A", rx, ry, 0, 1, 1, x2, y2,
						"z"].join(",")
			});

	this.translate(cx - bb.x - bb.width / 2, cy - bb.y - bb.height / 2);

	return p.insertBefore(this.node ? this : this[0]);
};

/*
 * Tooltips on Paper prototype
 */
/*
 * \ Paper.label [ method ] * Puts the given `text` into a 'label' tooltip. The
 * text is given a default style according to @g.txtattr. See @Element.label * >
 * Parameters * - x (number) x coordinate of the center of the label - y
 * (number) y coordinate of the center of the label - text (string) text to
 * place inside the label * = (object) set containing the label path and the
 * text element > Usage | paper.label(50, 50, "$9.99"); \
 */
Raphael.fn.label = function(x, y, text) {
	var set = this.set();

	text = this.text(x, y, text).attr(Raphael.g.txtattr);
	return set.push(text.label(), text);
};

/*
 * \ Paper.popup [ method ] * Puts the given `text` into a 'popup' tooltip. The
 * text is given a default style according to @g.txtattr. See @Element.popup
 * 
 * Note: The `dir` parameter has changed from g.Raphael 0.4.1 to 0.5. The
 * options `0`, `1`, `2`, and `3` has been changed to `'down'`, `'left'`,
 * `'up'`, and `'right'` respectively. * > Parameters * - x (number) x
 * coordinate of the popup's tail - y (number) y coordinate of the popup's tail -
 * text (string) text to place inside the popup - dir (string) location of the
 * text relative to the tail: `'down'`, `'left'`, `'up'` [default], or
 * `'right'`. - size (number) amount of padding around the Element [default:
 * `5`] * = (object) set containing the popup path and the text element > Usage |
 * paper.popup(50, 50, "$9.99", 'down'); \
 */
Raphael.fn.popup = function(x, y, text, dir, size) {
	var set = this.set();

	text = this.text(x, y, text).attr(Raphael.g.txtattr);
	return set.push(text.popup(dir, size), text);
};

/*
 * \ Paper.tag [ method ] * Puts the given text into a 'tag' tooltip. The text
 * is given a default style according to @g.txtattr. See @Element.tag * >
 * Parameters * - x (number) x coordinate of the center of the tag loop - y
 * (number) y coordinate of the center of the tag loop - text (string) text to
 * place inside the tag - angle (number) angle of orientation in degrees
 * [default: `0`] - r (number) radius of the loop [default: `5`] * = (object)
 * set containing the tag path and the text element > Usage | paper.tag(50, 50,
 * "$9.99", 60); \
 */
Raphael.fn.tag = function(x, y, text, angle, r) {
	var set = this.set();

	text = this.text(x, y, text).attr(Raphael.g.txtattr);
	return set.push(text.tag(angle, r), text);
};

/*
 * \ Paper.flag [ method ] * Puts the given `text` into a 'flag' tooltip. The
 * text is given a default style according to @g.txtattr. See @Element.flag * >
 * Parameters * - x (number) x coordinate of the flag's point - y (number) y
 * coordinate of the flag's point - text (string) text to place inside the flag -
 * angle (number) angle of orientation in degrees [default: `0`] * = (object)
 * set containing the flag path and the text element > Usage | paper.flag(50,
 * 50, "$9.99", 60); \
 */
Raphael.fn.flag = function(x, y, text, angle) {
	var set = this.set();

	text = this.text(x, y, text).attr(Raphael.g.txtattr);
	return set.push(text.flag(angle), text);
};

/*
 * \ Paper.drop [ method ] * Puts the given text into a 'drop' tooltip. The text
 * is given a default style according to @g.txtattr. See @Element.drop * >
 * Parameters * - x (number) x coordinate of the drop's point - y (number) y
 * coordinate of the drop's point - text (string) text to place inside the drop -
 * angle (number) angle of orientation in degrees [default: `0`] * = (object)
 * set containing the drop path and the text element > Usage | paper.drop(50,
 * 50, "$9.99", 60); \
 */
Raphael.fn.drop = function(x, y, text, angle) {
	var set = this.set();

	text = this.text(x, y, text).attr(Raphael.g.txtattr);
	return set.push(text.drop(angle), text);
};

/*
 * \ Paper.blob [ method ] * Puts the given text into a 'blob' tooltip. The text
 * is given a default style according to @g.txtattr. See @Element.blob * >
 * Parameters * - x (number) x coordinate of the blob's tail - y (number) y
 * coordinate of the blob's tail - text (string) text to place inside the blob -
 * angle (number) angle of orientation in degrees [default: `0`] * = (object)
 * set containing the blob path and the text element > Usage | paper.blob(50,
 * 50, "$9.99", 60); \
 */
Raphael.fn.blob = function(x, y, text, angle) {
	var set = this.set();

	text = this.text(x, y, text).attr(Raphael.g.txtattr);
	return set.push(text.blob(angle), text);
};
/**
 * zhanghuihua
 */
Raphael.fn.axis = function(x, y, length, from, to, steps, orientation, labels,
		type, dashsize) {
	return Raphael.g.axis(x, y, length, from, to, steps, orientation, labels,
			type, dashsize, this);
};
/**
 * Brightness functions on the Element prototype
 */
/*
 * \ Element.lighter [ method ] * Makes the context element lighter by
 * increasing the brightness and reducing the saturation by a given factor. Can
 * be called on Sets. * > Parameters * - times (number) adjustment factor
 * [default: `2`] * = (object) Element > Usage | paper.circle(50, 50, 20).attr({ |
 * fill: "#ff0000", | stroke: "#fff", | "stroke-width": 2 | }).lighter(6); \
 */
Raphael.el.lighter = function(times) {
	times = times || 2;

	var fs = [this.attrs.fill, this.attrs.stroke];

	this.fs = this.fs || [fs[0], fs[1]];

	fs[0] = Raphael.rgb2hsb(Raphael.getRGB(fs[0]).hex);
	fs[1] = Raphael.rgb2hsb(Raphael.getRGB(fs[1]).hex);
	fs[0].b = Math.min(fs[0].b * times, 1);
	fs[0].s = fs[0].s / times;
	fs[1].b = Math.min(fs[1].b * times, 1);
	fs[1].s = fs[1].s / times;

	this.attr({
				fill : "hsb(" + [fs[0].h, fs[0].s, fs[0].b] + ")",
				stroke : "hsb(" + [fs[1].h, fs[1].s, fs[1].b] + ")"
			});
	return this;
};

/*
 * \ Element.darker [ method ] * Makes the context element darker by decreasing
 * the brightness and increasing the saturation by a given factor. Can be called
 * on Sets. * > Parameters * - times (number) adjustment factor [default: `2`] * =
 * (object) Element > Usage | paper.circle(50, 50, 20).attr({ | fill: "#ff0000", |
 * stroke: "#fff", | "stroke-width": 2 | }).darker(6); \
 */
Raphael.el.darker = function(times) {
	times = times || 2;

	var fs = [this.attrs.fill, this.attrs.stroke];

	this.fs = this.fs || [fs[0], fs[1]];

	fs[0] = Raphael.rgb2hsb(Raphael.getRGB(fs[0]).hex);
	fs[1] = Raphael.rgb2hsb(Raphael.getRGB(fs[1]).hex);
	fs[0].s = Math.min(fs[0].s * times, 1);
	fs[0].b = fs[0].b / times;
	fs[1].s = Math.min(fs[1].s * times, 1);
	fs[1].b = fs[1].b / times;

	this.attr({
				fill : "hsb(" + [fs[0].h, fs[0].s, fs[0].b] + ")",
				stroke : "hsb(" + [fs[1].h, fs[1].s, fs[1].b] + ")"
			});
	return this;
};

/*
 * \ Element.resetBrightness [ method ] * Resets brightness and saturation
 * levels to their original values. See @Element.lighter and @Element.darker.
 * Can be called on Sets. * = (object) Element > Usage | paper.circle(50, 50,
 * 20).attr({ | fill: "#ff0000", | stroke: "#fff", | "stroke-width": 2 |
 * }).lighter(6).resetBrightness(); \
 */
Raphael.el.resetBrightness = function() {
	if (this.fs) {
		this.attr({
					fill : this.fs[0],
					stroke : this.fs[1]
				});
		delete this.fs;
	}
	return this;
};

// alias to set prototype
(function() {
	var brightness = ['lighter', 'darker', 'resetBrightness'], tooltips = [
			'popup', 'tag', 'flag', 'label', 'drop', 'blob'];

	for (var f in tooltips)
		(function(name) {
			Raphael.st[name] = function() {
				return Raphael.el[name].apply(this, arguments);
			};
		})(tooltips[f]);

	for (var f in brightness)
		(function(name) {
			Raphael.st[name] = function() {
				for (var i = 0; i < this.length; i++) {
					this[i][name].apply(this[i], arguments);
				}

				return this;
			};
		})(brightness[f]);
})();

// chart prototype for storing common functions
Raphael.g = {
	/*
	 * \ g.shim [ object ] * An attribute object that charts will set on all
	 * generated shims (shims being the invisible objects that mouse events are
	 * bound to) * > Default value | { stroke: 'none', fill: '#000',
	 * 'fill-opacity': 0 } \
	 */
	shim : {
		stroke : 'none',
		fill : '#000',
		'fill-opacity' : 0
	},

	/*
	 * \ g.txtattr [ object ] * An attribute object that charts and tooltips
	 * will set on any generated text * > Default value | { font: '12px Arial,
	 * sans-serif', fill: '#fff' } \
	 */
	txtattr : {
		font : '12px Arial, sans-serif',
		fill : '#fff'
	},

	/*
	 * \ g.colors [ array ] * An array of color values that charts will iterate
	 * through when drawing chart data values. * \
	 */
	colors : (function() {
		var hues = [.6, .2, .05, .1333, .75, 0], colors = [];

		for (var i = 0; i < 10; i++) {
			if (i < hues.length) {
				colors.push('hsb(' + hues[i] + ',.75, .75)');
			} else {
				colors.push('hsb(' + hues[i - hues.length] + ', 1, .5)');
			}
		}

		return colors;
	})(),

	snapEnds : function(from, to, steps) {
		var f = from, t = to;

		if (f == t) {
			return {
				from : f,
				to : t,
				power : 0
			};
		}

		function round(a) {
			return Math.abs(a - .5) < .25 ? ~~(a) + .5 : Math.ceil(a);
		}

		var d = (t - f) / steps, r = ~~(d), R = r, i = 0;

		if (r) {
			while (R) {
				i--;
				R = ~~(d * Math.pow(10, i)) / Math.pow(10, i);
			}

			i++;
		} else {
			while (!r) {
				i = i || 1;
				r = ~~(d * Math.pow(10, i)) / Math.pow(10, i);
				i++;
			}

			i && i--;
		}

		t = round(to * Math.pow(10, i)) / Math.pow(10, i);

		if (t < to) {
			t = round((to + .5) * Math.pow(10, i)) / Math.pow(10, i);
		}

		f = round((from - (i > 0 ? 0 : .5)) * Math.pow(10, i))
				/ Math.pow(10, i);
		return {
			from : f,
			to : t,
			power : i
		};
	},

	axis : function(x, y, length, from, to, steps, orientation, labels, type,
			dashsize, paper) {
		dashsize = dashsize == null ? 2 : dashsize;
		type = type || "t";
		steps = steps || 10;
		paper = arguments[arguments.length - 1] // paper is always last argument

		var path = type == "|" || type == " "
				? ["M", x + .5, y, "l", 0, .001]
				: orientation == 1 || orientation == 3 ? ["M", x + .5, y, "l",
						0, -length] : ["M", x, y + .5, "l", length, 0], ends = this
				.snapEnds(from, to, steps), f = ends.from, t = ends.to, i = ends.power, j = 0, txtattr = {
			font : "11px 'Fontin Sans', Fontin-Sans, sans-serif"
		}, text = paper.set(), d;

		d = (t - f) / steps;

		var label = f, rnd = i > 0 ? i : 0;
		dx = length / steps;

		if (+orientation == 1 || +orientation == 3) {
			var Y = y, addon = (orientation - 1 ? 1 : -1)
					* (dashsize + 3 + !!(orientation - 1));

			while (Y >= y - length) {
				type != "-"
						&& type != " "
						&& (path = path.concat([
								"M",
								x
										- (type == "+" || type == "|"
												? dashsize
												: !(orientation - 1) * dashsize
														* 2), Y + .5, "l",
								dashsize * 2 + 1, 0]));
				text.push(paper.text(
						x + addon,
						Y,
						(labels && labels[j++])
								|| (Math.round(label) == label ? label : +label
										.toFixed(rnd))).attr(txtattr).attr({
							"text-anchor" : orientation - 1 ? "start" : "end"
						}));
				label += d;
				Y -= dx;
			}

			if (Math.round(Y + dx - (y - length))) {
				type != "-"
						&& type != " "
						&& (path = path.concat([
								"M",
								x
										- (type == "+" || type == "|"
												? dashsize
												: !(orientation - 1) * dashsize
														* 2), y - length + .5,
								"l", dashsize * 2 + 1, 0]));
				text.push(paper.text(
						x + addon,
						y - length,
						(labels && labels[j])
								|| (Math.round(label) == label ? label : +label
										.toFixed(rnd))).attr(txtattr).attr({
							"text-anchor" : orientation - 1 ? "start" : "end"
						}));
			}
		} else {
			label = f;
			rnd = (i > 0) * i;
			addon = (orientation ? -1 : 1) * (dashsize + 9 + !orientation);

			var X = x, dx = length / steps, txt = 0, prev = 0;

			while (X <= x + length) {
				type != "-"
						&& type != " "
						&& (path = path
								.concat([
										"M",
										X + .5,
										y
												- (type == "+"
														? dashsize
														: !!orientation
																* dashsize * 2),
										"l", 0, dashsize * 2 + 1]));
				text.push(txt = paper.text(
						X,
						y + addon,
						(labels && labels[j++])
								|| (Math.round(label) == label ? label : +label
										.toFixed(rnd))).attr(txtattr));

				var bb = txt.getBBox();

				if (prev >= bb.x - 5) {
					text.pop(text.length - 1).remove();
				} else {
					prev = bb.x + bb.width;
				}

				label += d;
				X += dx;
			}

			if (Math.round(X - dx - x - length)) {
				type != "-"
						&& type != " "
						&& (path = path
								.concat([
										"M",
										x + length + .5,
										y
												- (type == "+"
														? dashsize
														: !!orientation
																* dashsize * 2),
										"l", 0, dashsize * 2 + 1]));
				text.push(paper.text(
						x + length,
						y + addon,
						(labels && labels[j])
								|| (Math.round(label) == label ? label : +label
										.toFixed(rnd))).attr(txtattr));
			}
		}

		var res = paper.path(path);

		res.text = text;
		res.all = paper.set([res, text]);
		res.remove = function() {
			this.text.remove();
			this.constructor.prototype.remove.call(this);
		};

		return res;
	},

	labelise : function(label, val, total) {
		if (label) {
			return (label + "").replace(/(##+(?:\.#+)?)|(%%+(?:\.%+)?)/g,
					function(all, value, percent) {
						if (value) {
							return (+val)
									.toFixed(value.replace(/^#+\.?/g, "").length);
						}
						if (percent) {
							return (val * 100 / total).toFixed(percent.replace(
									/^%+\.?/g, "").length)
									+ "%";
						}
					});
		} else {
			return (+val).toFixed(0);
		}
	}
}
